#include "TreeOfNodes.h"

#include <deque>

namespace jcrada {

    TreeOfNodes::TreeOfNodes() :
    _root(NULL),
    _fitness(0.0) {

    }

    TreeOfNodes::TreeOfNodes(Node* root) :
    _root(root),
    _fitness(0.0) {

    }

    TreeOfNodes::~TreeOfNodes() {
        if (_root) {
            delete _root;
        }
    }

    TreeOfNodes* TreeOfNodes::clone() const {
        TreeOfNodes* result = new TreeOfNodes();
        result->setFitness(getFitness());
        if (getRoot()) {
            result->setRoot(getRoot()->clone());
        }
        return result;
    }

    std::string TreeOfNodes::toString() const {
        std::string result = "Tree={f=" + TO_STRING(getFitness()) + ";";
        if (getRoot()) {
            result += getRoot()->toString();
        }
        result += "}";
        return result;
    }

    Node* TreeOfNodes::findNodeUsingBFS(int position) const {
        return getRoot() ? getRoot()->findNodeInSubtreeUsingBFS(position) : NULL;
    }

    int TreeOfNodes::findPositionUsingBFS(const Node* node) const {
        return getRoot() ? getRoot()->findPositionInSubtreeUsingBFS(node) : -1;
    }

    std::vector<FunctionNode*>* TreeOfNodes::getFunctionNodes() const {
        std::vector<FunctionNode*>* result = new std::vector<FunctionNode*>;
        FunctionNode* root_function = dynamic_cast<FunctionNode*> (getRoot());
        if (root_function) {
            result->push_back(root_function);
        }
        //BFS
        Node* n = NULL;
        Node* child = NULL;
        std::deque<Node*> deque;
        deque.push_back(getRoot());
        while (deque.size() > 0) {
            n = deque.front();
            for (int i = 0; i < n->getArity(); ++i) {
                child = n->getChild(i);
                if (child != NULL) {
                    deque.push_back(child);
                    FunctionNode* d_cast = dynamic_cast<FunctionNode*> (child);
                    if (d_cast) {
                        result->push_back(d_cast);
                    }
                }
            }
            deque.pop_front();
        }
        return result;
    }

    std::vector<TerminalNode*>* TreeOfNodes::getTerminalNodes() const {
        std::vector<TerminalNode*>* result = new std::vector<TerminalNode*>;
        TerminalNode* root_terminal = dynamic_cast<TerminalNode*> (getRoot());
        if (root_terminal) {
            result->push_back(root_terminal);
        }
        //BFS
        Node* n = NULL;
        Node* child = NULL;
        std::deque<Node*> deque;
        deque.push_back(getRoot());
        while (deque.size() > 0) {
            n = deque.front();
            for (int i = 0; i < n->getArity(); ++i) {
                child = n->getChild(i);
                if (child != NULL) {
                    deque.push_back(child);
                    TerminalNode* d_cast = dynamic_cast<TerminalNode*> (child);
                    if (d_cast) {
                        result->push_back(d_cast);
                    }
                }
            }
            deque.pop_front();
        }
        return result;
    }

    void TreeOfNodes::toXml(QDomDocument& xml, QDomElement* parent) {
        QDomElement tree = xml.createElement("TreeOfNodes");
        tree.setAttribute("fitness", getFitness());
        if (getRoot()) {
            getRoot()->toXml(xml, &tree);
        }
        if (parent) {
            parent->appendChild(tree);
        } else {
            xml.appendChild(tree);
        }
    }

}

